package mtm.date;

import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;

public class MtmDate implements Comparable<MtmDate>, Serializable {

	private Calendar calendar = Calendar.getInstance();
	
	
	/**
	 * Constructor with initial values
	 * @param year
	 * @param month
	 * @param date
	 * @param hour
	 * @param minute
	 */
	public MtmDate(int year, int month, int date, int hour, int minute) {
		calendar.set(Calendar.YEAR, year);
		calendar.set(Calendar.MONTH, month - 1);
		calendar.set(Calendar.DATE, date);
		calendar.set(Calendar.HOUR_OF_DAY, hour);
		calendar.set(Calendar.MINUTE, minute);
	}
	
	
	/**
	 * Constructor
	 * @param year
	 * @param month
	 * @param date
	 */
	public MtmDate(int year, int month, int date) {
		this(year, month, date, 0, 0);
	}
	
	
	/**
	 * Constructor based on another MtmDate object
	 * @param date
	 */
	public MtmDate(MtmDate date) {
		calendar.set(Calendar.YEAR, date.getYear());
		calendar.set(Calendar.MONTH, date.getMonth() - 1);
		calendar.set(Calendar.DATE, date.getDate());
		calendar.set(Calendar.HOUR_OF_DAY, date.getHours());
		calendar.set(Calendar.MINUTE, date.getMinutes());
	}
	
	
	/**
	 * Constructor based on another Date object
	 * @param date
	 */
	public MtmDate(Date date) {
		calendar.setTime(date);
		calendar.set(Calendar.HOUR_OF_DAY, 23);
		calendar.set(Calendar.MINUTE, 59);
	}
	
	
	/**
	 * The current date
	 */
	public MtmDate() {
		calendar.set(Calendar.MONTH, getMonth() - 1);
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		calendar.set(Calendar.MINUTE, 0);
	}
	
	
	/**
	 * Return a MtmDate object from formatted string "y-M-d h:m"
	 * @param date
	 * @return
	 */
	public static MtmDate createFromString(String dateString) {
		if (dateString == null || dateString.trim().equals(""))
			return null;
		
		StringTokenizer token = new StringTokenizer(dateString, " ");
		String day = token.nextToken();
		
		// get day information
		StringTokenizer dayToken = new StringTokenizer(day, "-");
		int year = Integer.parseInt(dayToken.nextToken());
		int month = Integer.parseInt(dayToken.nextToken());
		int date = Integer.parseInt(dayToken.nextToken());
		
		String time;
		try {
			time = token.nextToken();
		} catch (NoSuchElementException ex) {
			return new MtmDate(year, month, date);
		}
		
		// get time information
		StringTokenizer timeToken = new StringTokenizer(time, ":");
		int hour = Integer.parseInt(timeToken.nextToken());
		int minute = Integer.parseInt(timeToken.nextToken());
		
		return new MtmDate(year, month, date, hour, minute);
	}
	
	
	/**
	 * Return and MtmDate object from a Date object and a time ("h:m") string
	 * @param date
	 * @param time
	 * @return
	 */
	public static MtmDate createFromDateAndTime(Date date, String time) {
		// get time information
		StringTokenizer timeToken = new StringTokenizer(time, ":");
		int hour = Integer.parseInt(timeToken.nextToken());
		int minute = Integer.parseInt(timeToken.nextToken());
		
		// get date information
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		
		return new MtmDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1,
				cal.get(Calendar.DATE), hour, minute);
	}
	
	
	/**
	 * Return the first day of current week
	 * @return
	 */
	public static MtmDate getFirstDayOfCurrentWeek() {
		MtmDate currentDate = new MtmDate();
		currentDate.addDate(-(currentDate.getDay() - 1));
		
		return currentDate;
	}
	
	
	/**
	 * Return first day of week which the correlative date appears
	 * @return
	 */
	public static MtmDate getFirstDayOfWeek(MtmDate date) {
		MtmDate result = new MtmDate(date);
		result.addDate(-(result.getDay() - 1));
		
		return result;
	}
	
	
	@Override
	public String toString() {
		String month = (getMonth() < 10) ? ("0" + getMonth()) : "" + getMonth();
		String date = (getDate() < 10) ? ("0" + getDate()) :  ("" + getDate());
		
		String hours = (getHours() < 10) ? ("0" + getHours()) : ("" + getHours());
		String minutes = (getMinutes() < 10) ? ("0" + getMinutes()) : ("" + getMinutes());
		return getYear() + "-" + month + "-" + date + " " 
					+ hours + ":" + minutes;
	}
	
	
	/**
	 * Day format of this MtmDate object 
	 * @return
	 */
	public Date getDayFormat() {
		Calendar cal = Calendar.getInstance();
		cal.set(Calendar.YEAR, getYear());
		cal.set(Calendar.MONTH, getMonth() - 1);
		cal.set(Calendar.DATE, getDate());
		
		return cal.getTime();
	}
	
	
	/**
	 * Time format of this MtmDate object ("h:m")
	 * @return
	 */
	public String getTimeFormat() {
		String hours = (getHours() < 10) ? ("0" + getHours()) : ("" + getHours());
		String minutes = (getMinutes() < 10) ? ("0" + getMinutes()) : ("" + getMinutes());
		return hours + ":" + minutes;
	}
	
	
	/**
	 * Compare two MtmDate objects
	 * @param o
	 * @return
	 */
	public int compareTo(MtmDate anotherDate) {
		if (getYear() != anotherDate.getYear()) {
			return getYear() - anotherDate.getYear();
		} else if (getMonth() != anotherDate.getMonth()) {
			return getMonth() - anotherDate.getMonth();
		} 
		else if (getDate() != anotherDate.getDate()) {
			return getDate() - anotherDate.getDate();
		} else {
			return getHours() * 60 + getMinutes() 
					- anotherDate.getHours() * 60 - anotherDate.getMinutes(); 
		}
		
	}
	
	
//	/**
//	 * Calculate how many days between these 2 dates
//	 * @param anotherDate
//	 * @return
//	 */
//	public int subtract(MtmDate anotherDate) {
//		return (int) ((calendar.getTime().getTime() - anotherDate.calendar.getTime().getTime()) / 86400000);
//	}
	
	
	/**
	 * Check if this MtmDate object is the curretn date or not?
	 * @return
	 */
	public boolean isCurrentDate() {
		return compareTo(new MtmDate()) == 0;
	}
	
	
	/**
	 * Return day of this MtmDate object
	 * 1 means Sunday
	 * 2 means Monday 
	 * ...
	 * @return
	 */
	public int getDay() {
		return calendar.get(Calendar.DAY_OF_WEEK);
	}
	
	
	/**
	 * Getter methods
	 */
	public int getYear() {
		return calendar.get(Calendar.YEAR);
	}
	
	public int getMonth() {
		return calendar.get(Calendar.MONTH) + 1;
	}
	
	public int getDate() {
		return calendar.get(Calendar.DATE);
	}
	
	public int getHours() {
		return calendar.get(Calendar.HOUR_OF_DAY);
	}
	
	public int getMinutes() {
		return calendar.get(Calendar.MINUTE);
	}
	
	
	/**
	 * Setter methods
	 */
	public void setYear(int year) {
		calendar.set(Calendar.YEAR, year);
	}
	
	public void setMonth(int month) {
		calendar.set(Calendar.MONTH, month - 1);
	}
	
	public void setDate(int date) {
		calendar.set(Calendar.DATE, date);
	}
	
	public void setHours(int hours) {
		calendar.set(Calendar.HOUR_OF_DAY, hours);
	}
	
	public void setMinutes(int minutes) {
		calendar.set(Calendar.MINUTE, minutes);
	}
	
	
	/**
	 * Add time method, use to inscrease or decrease value of this date
	 */
	public void addYear(int year) {
		calendar.set(Calendar.YEAR, getYear() + year);
	}
	
	/**
	 * Note month must be non-negative integer
	 * @param month
	 */
	public void addMonth(int month) {
		int temp = month + getMonth();
		if (temp <= 12) {
			setMonth(temp);
		} else {
			addYear(temp / 12);
			setMonth(temp % 12);
		}
	}
	
	public void addDate(int date) {
		calendar.set(Calendar.DATE, getDate() + date);
	}
	
	public void addHour(int hour) {
		calendar.set(Calendar.HOUR_OF_DAY, getHours() + hour);
	}
	
	public void addMinute(int minute) {
		calendar.set(Calendar.MINUTE, getMinutes() + minute);
	}
}
